#include "book.h"
#include "ui_book.h"

#include <QUrl>
#include<QObject>
#include<QFile>
#include<QThread>
#include<QTextStream>
#include<QQueue>
#include<QFileInfo>
#include <QNetworkAccessManager>
#include<QNetworkReply>
#include<QNetworkRequest>
#include <QFileDialog>
#include <QIcon>
#include <QEventLoop>
#include <QTimer>
#include <QCryptographicHash>
#include <QMessageBox>
#include <QModelIndex>
#include<QStandardItem>
#include <QStandardItemModel>
#include <QStringListModel>
#include <Qpair>
Book::Book(QWidget* parent)
	: QMainWindow(parent)
	, ui(new Ui::Book)
{
	ui->setupUi(this);
	//成员变量初始化
	manager = new QNetworkAccessManager(this);
	ui->progressBar->setRange(0, 100);


	//这是一个默认路径
	filepath = "D:/QTcode/image/new";
	connect(ui->listView, &QListView::clicked, this, &Book::on_listview_clicked);
    //connect(this,&Book::downLoadPause,this,&Book::on_downLoadPause);
	ui->urledit->setText("https://download.virtualbox.org/virtualbox/7.0.18/VirtualBox-7.0.18-162988-Win.exe?source=:ow:o:p:nav:mmddyyVirtualBoxHero_cn");
}

Book::~Book()
{
	delete ui;
}

void Book::on_searchbtn_clicked()
{
	//对展示页面的筛选操作
	//输入一个医生，筛选医生名下的设计，需要一个结构体？
}

void Book::on_filepathbtn_clicked()
{
	QString filename;
	//选择下载的文件夹
	QUrl filepath1 = QFileDialog::getExistingDirectoryUrl(this, tr("open file"), QUrl(""), QFileDialog::ShowDirsOnly);
	filepath = filepath1.toString();
	filepath = filepath.remove("file:///");
	ui->textEdit->append("选择下载文件的文件夹路径为：" + filepath);
}


void Book::on_updatebtn_clicked()
{

}
void Book::on_urlbtn_clicked()
{
	//设置下载文件资源的url，获取文件名，获取文件的大小
	QUrl url = ui->urledit->text();
	qDebug() << "url:" << url;

	//获取文件名的函数/获取url的路径部分
	QString path = url.path();
	QFileInfo fileInfo(path);
	filename = fileInfo.fileName();
	qDebug() << "文件名：" << filename;
	filenames.push_back(filename);
	getFileSize();
	//新增文件后的显示代码
    fileListShow();
}

void Book::on_listview_clicked()
{

}

void Book::getFileSize()
{
	QUrl url = ui->urledit->text();
	//获取文件的大小,请求头中的数据
	QNetworkRequest request;
	request.setUrl(url);
	qint64 filesize_ = 0;
	QEventLoop loop;
	QNetworkReply* reply = manager->head(request);
	connect(reply, &QNetworkReply::finished, this, [&]() {
		//返回结果成功
		if (reply->error() == QNetworkReply::NoError) {
			// 获取响应头中的Content-Length字段来获取文件大小
			QVariant contentLength = reply->header(QNetworkRequest::ContentLengthHeader);
			if (contentLength.isValid()) {
				filesize_ = contentLength.toLongLong();
				filesize = filesize_;
				//这边获取的是整个finish的信号
				qDebug() << "文件大小111：" << filesize_;
				loop.quit();
			}
			else {
				qWarning() << "无法获取文件大小，Content-Length不存在";
			}
		}
		else {
			//qDebug() << "Host not found. Could not connect to the server.";
			QMessageBox::warning(this, "错误", "发送请求失败！");
		}
		reply->deleteLater();//之前在外部删除reply，导致没接收到reply信号就被删除了
		});
	//下载操作，小文件直接使用downloadACtion，大文件呢
	loop.exec();
	//将文件分成100个块S

	qint64 blocksize = filesize / totalblocks;
	qint64 lastsize = filesize % totalblocks;

	qDebug() << "blocksize:" << blocksize;
	qDebug() << "lastsize:" << lastsize;
	//如何将100个请求分成10*10=100呢？使用一个请求队列，每次发出10个请求

	//记录100个文件的起始字节数
	for (int i = 0; i < 100; i++) {
		qint64 startpoint = i * blocksize;
		qint64 endpoint = qMin(startpoint + blocksize - 1, filesize - 1);
		requestqueue.enqueue(QPair<qint64, qint64>(startpoint, endpoint));
	}

}

void Book::setHeadRange()
{

}

void Book::on_downloadprogress()
{
	//获取当前的下载进度，在页面中展示出来，需要多线程吗
}

void Book::caluculateMd5()
{
	//这是一个计算文件MD5的函数
	//打开文件
	QFile file(filepath + "/" + filename);
	if (!file.open(QIODevice::ReadOnly)) {
		QMessageBox::information(this, "提示", "文件打开失败，无法计算MD5值");
		return;
	}
	QCryptographicHash realmd5(QCryptographicHash::Md5);
	realmd5.reset();
	//读取文件，计算MD5
	while (!file.atEnd()) {
		realmd5.addData(file.readAll());
	}
	qDebug() << realmd5.result().toHex();

}

void Book::on_downLoadPause()
{
//
}

///
/// \brief Book::downLoadAction 用于获取到url后下载的函数
/// \param url  用于文件下载的url
/// \param filepath  文件存储的父路径
/// \param filename  下载文件的文件名
///
void Book::downLoadAction(const QUrl url, const QString filepath, const QString filename)
{

	//下载函数
   // QNetworkAccessManager * manager=new QNetworkAccessManager(this);
	QNetworkRequest request;
	//发送请求
	request.setUrl(url);
	//发送请求后设置
	QFile file(filepath + "/" + filename);
	QNetworkReply* reply1 = manager->get(request);

	connect(reply1, &QNetworkReply::finished, this, [=]() {
		//这边写入文件
		if (reply1->error() == QNetworkReply::NoError) {
			//error信号在那个阶段能发出，
			qDebug() << "请求发送成功";
			if (!filepath.isEmpty()) {
				QFile file(filepath + "/" + filename);
				if (file.exists()) {
					//判断该文件是否存在
					QMessageBox::information(this, "提示", "文件已经存在");
					return;
				}
				qDebug() << filepath + "/" + filename;
				QByteArray imageData = reply1->readAll();
				if (file.open(QIODevice::WriteOnly)) {
					file.write(imageData);  // 将收到的数据写入文件
					//file.flush();
					file.close();
					QMessageBox::information(this, "成功", "下载成功！");
				}
				else {
					QMessageBox::warning(this, "错误", "无法打开文件进行写入！");
				}

			}
			else {
				QMessageBox::warning(this, "下载失败", "下载失败：" + reply1->errorString());
			}
			QByteArray imageData = reply1->readAll();
			qDebug() << imageData.size();
			reply1->deleteLater();
		}
		else {
			qDebug() << "请求失败: " << reply1->errorString();
		}


		// 检测是否连接到服务器失败
		if (reply1->error() == QNetworkReply::HostNotFoundError) {
			qDebug() << "Host not found. Could not connect to the server.";
		}
		else if (reply1->error() == QNetworkReply::TimeoutError) {
			qDebug() << "Connection timed out.";
		}
		else if (reply1->error() == QNetworkReply::SslHandshakeFailedError) {
			qDebug() << "Ssl handle failed.";
		}
		else if (reply1->error() == QNetworkReply::TemporaryNetworkFailureError) {
			qDebug() << "network failed.";
		}


		});
	// connect(reply1,&QNetworkReply::finished,this,[=](){
	//     reply1->deleteLater();
	// });

}

void Book::downLoadAction_2(const QUrl url, const QString filepath, const QString filename)
{
	//下载函数
	qDebug() << "filesize:" << filesize << "filesize/4" << filesize / 10;
	qint64 blocksize_2 = filesize / 10;
	qint64 lastsize = filesize % 10;
	//endoffset初始化，肯定大于4吧
	qint64 endoffset = 0;
	qDebug() << "block_size_2:" << blocksize_2;
	qDebug() << "lastsize:" << lastsize;
	//发送请求后设置
	//使用range将数据分块下载
	if (blocksize_2 != 0) {
		//初始化第一次的endoffset
		endoffset = qMin(currentoffset + blocksize_2 - 1, filesize - 1);
	}
	else {
		endoffset = lastsize;
	}
	//循环5次，每次发送一个请求
	//创建一个文件
	QFile file(filepath + "/" + filename);
	qDebug() << filepath + "/" + filename;
	if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) {
		QMessageBox::information(this, "失败", "打开文件失败！");
		return;
	}
	// int index=0;
	// downoadChunk(url,file,blocksize_2,endoffset,index);
	//每个文件都循环5次
	for (int i = 0; i < 11; i++) {
		//设置请求头
		endoffset = qMin(currentoffset + blocksize_2 - 1, filesize - 1);
		QNetworkRequest request;
		request.setUrl(url);
		request.setRawHeader("Range", QString("bytes=%1-%2").arg(currentoffset).arg(endoffset).toUtf8());
		qDebug() << "currentoffset:" << currentoffset;
		qDebug() << "endoffset:" << endoffset;
		qDebug() << "hand:" << request.rawHeader("Range");
		QEventLoop loop;

		QNetworkReply* reply = manager->get(request);

		connect(reply, &QNetworkReply::finished, this, [=, &loop, &file, &i]()mutable {
			//引用捕获，值捕获
			QByteArray data = reply->readAll();
			qDebug() << "readyread:" << i << data.size();
			file.write(data);
			file.flush();
			qDebug() << "finished:" << i;
			currentoffset = endoffset + 1;
			endoffset = qMin(currentoffset + blocksize_2 - 1, filesize - 1);
			//找到此次上传文件的结束位置，filesize-1控制不超过文件最后一个字符
			reply->deleteLater();
			loop.quit();
			});
		loop.exec();
	}
	// 关闭文件在所有请求完成后
	connect(manager, &QNetworkAccessManager::finished, this, [this, &file]() {
		file.close();
		if (file.size() != 0) {
			qDebug() << "final filesize:" << file.size();
			QMessageBox::information(this, "成功", "下载成功！");
		}
		});

}

void Book::downLoadAction_3(const QUrl url, const QString filepath, const QString filename)
{
	//这个里边存储reply对象
	QVector<QByteArray> replylist;
	//将文件打开
	QFile file(filepath + "/" + filename);
    if (file.exists()&&file.size()==filesize) {
		//文件已经存在
		QMessageBox::information(this, "提示", "文件已经存在");
		return;
	}
	qDebug() << filepath + "/" + filename;
    if (!file.open(QIODevice::WriteOnly |QIODevice::Append)) {
		QMessageBox::information(this, "失败", "打开文件失败！");
		return;
	}
	const int myvariant = 10;
    for (int i = 0; i < 10 && !requestqueue.isEmpty()&&downloadstatus; i++) {
		qDebug() << "i=" << i;
		QEventLoop loop;
		int a = myvariant;
		QVector<QNetworkRequest> requestlists;
        replylist.resize(myvariant);
        requestlists.resize(15);
        //当我点击暂停下载信号后，downloadstatus为false
        for (int j = 0; j < myvariant&&downloadstatus; j++) {
			//set请求头
			QNetworkRequest &request= requestlists[j];
			QPair<qint64, qint64> mypair = requestqueue.dequeue();//出队
			request.setUrl(url);
			request.setRawHeader("Range", QString("bytes=%1-%2").arg(mypair.first).arg(mypair.second).toUtf8());
			qDebug() << "i=" << i << "j=" << j << "begin:" << mypair.first << "end:" << mypair.second;
			QNetworkReply* reply = manager->get(request);
			//将j和对应的信号连接起来，
            connect(reply, &QNetworkReply::finished, [=, &loop, &file, &a,&replylist]() {
				//接收到的reply信号放进来
				replylist[j] = reply->readAll();
				qDebug() << replylist[j].size();
				reply->deleteLater();
				qDebug() << "finish i=" << i << "j=" << j;
				a--;
				if (a == 0) {
					loop.quit();
				}
				});
		}
		loop.exec();
		qDebug() << replylist.size();
        //当list中的元素完整了才往文件里写
		QEventLoop loop1;
		//connect(ui->progressBar, &QProgressBar::valueChanged, this, [=]() {
		//	ui->progressBar->setValue(progressvalue);
		//	});
        for (int k = 0; (k < myvariant)&&(replylist.size()==10); k++) {
			
			file.write(replylist[k]);
            qDebug()<<replylist[k].size();
            progressvalue=(file.size()*100) / filesize;
            qDebug() <<"progressvalue:"<< progressvalue;
            ui->progressBar->setValue(progressvalue);
		}
        qDebug()<<"current filesize"<<file.size();
		//10块中每一块的数据接收完毕都可以更新一下进度条

	}

	
	file.close();
	qDebug() << "退出循环"<<"文件大小为："<<file.size()<<filesize;

	//确认文件是否完整
	// //MD5确认
	if (file.size() == filesize) {
		QMessageBox::information(this, "提示", "文件完整下载，下载成功");
		ui->pausebtn->setText("下载完成");
	}
	//大小确认
	caluculateMd5();
}



void Book::fileListShow()
{
    ///这块的展示内容怎么获得呢？
    ///1.首先请求该路径下的所有文件信息，需要得到文件名信息，患者信息，医生信息，做的修复类型信息
    ///
	model1 = new QStandardItemModel(this);
	//文件名列表初始化,主要用于展示
	filenames.push_back("file.txt");
	filenames.push_back("file2.txt");
	filenames.push_back("file3.txt");
	filenames.push_back("file4.txt");
	filenames.push_back("file5.txt");
	//需要为每一行文件增添一个QStanditem对象
	for (int i = 0; i < filenames.count(); i++) {
		QStandardItem* item = new QStandardItem;
		item->setText(filenames.at(i));
        item->setIcon(QIcon(":/image/floder.png"));
		model1->appendRow(item);
	}
	// 将模型设置到 listView 中
	ui->listView->setModel(model1);

	// 设置为垂直列表模式
	ui->listView->setViewMode(QListView::ListMode);

	// 显示 listView
	ui->listView->show();

}


void Book::on_pausebtn_clicked()
{
    //点击这个按钮后，获取当前的下载进度，并显示暂停下载，继续发送剩下的数据包，显示按钮的文本变为继续下载
	
    //中断的时候中断发送请求，但不丢弃当前已经下载的数据，将文件关闭？
    //文件在其他地方关闭，此时需要做的是暂停进度条和暂停发送剩下的请求
    //状态为false，不允许下载
    downloadstatus=!downloadstatus;
	if (downloadstatus == true) {
		ui->pausebtn->setText("暂停下载");
	}
	else {
		ui->pausebtn->setText("继续下载");
	}
    
    //状态转变后继续下载要重新调用下载函数
    qDebug()<<"此时的下载状态为："<<downloadstatus;
    if(downloadstatus==true){
		qDebug() << "url:" << ui->urledit->text() << "filepath:" << filepath << "filename:" << filename;
        downLoadAction_3(ui->urledit->text(),filepath,filename);
    }
}


void Book::on_downloadbtn_clicked()
{
    QUrl url = ui->urledit->text();
    //获取文件名的函数/获取url的路径部分
    QString path = url.path();
    qDebug() << "path:" << path;
    QFileInfo fileInfo(path);
    QString fileName = fileInfo.fileName();
    downLoadAction_3(url, filepath, fileName);
}

